package android.slc.box.rxlifecycle;

import autodispose2.lifecycle.CorrespondingEventsFunction;
import autodispose2.lifecycle.LifecycleEndedException;
import autodispose2.lifecycle.LifecycleNotStartedException;
import autodispose2.lifecycle.LifecycleScopeProvider;
import autodispose2.lifecycle.LifecycleScopes;
import io.reactivex.rxjava3.annotations.Nullable;
import io.reactivex.rxjava3.core.CompletableSource;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.subjects.BehaviorSubject;

/**
 * simple utility to create {@link LifecycleScopeProvider} instances for simples.
 *
 * <p>Supports a start and stop lifecycle. Subscribing when outside of the lifecycle will throw
 * either a {@link LifecycleNotStartedException} or {@link LifecycleEndedException}.
 */
public final class SimpleLifecycleScopeProvider
        implements LifecycleScopeProvider<SimpleLifecycleScopeProvider.SimpleLifecycle> {

    private final BehaviorSubject<SimpleLifecycle> lifecycleSubject;

    private SimpleLifecycleScopeProvider(@Nullable SimpleLifecycleScopeProvider.SimpleLifecycle initialValue) {
        if (initialValue == null) {
            lifecycleSubject = BehaviorSubject.create();
        } else {
            lifecycleSubject = BehaviorSubject.createDefault(initialValue);
        }
    }

    /**
     * Returns a new {@link SimpleLifecycleScopeProvider} instance.
     */
    public static SimpleLifecycleScopeProvider create() {
        return new SimpleLifecycleScopeProvider(null);
    }

    /**
     * Returns a new {@link SimpleLifecycleScopeProvider} instance with {@code simpleLifecycle.STARTED} as its
     * initial lifecycle event.
     */
    public static SimpleLifecycleScopeProvider createStart() {
        return new SimpleLifecycleScopeProvider(SimpleLifecycle.STARTED);
    }

    /**
     * Returns a new {@link SimpleLifecycleScopeProvider} instance with {@code initialValue} as its
     * initial lifecycle event.
     *
     * @param initialValue the initial lifecycle event to create the {@link
     *                     SimpleLifecycleScopeProvider} with.
     */
    public static SimpleLifecycleScopeProvider createInitial(SimpleLifecycle initialValue) {
        return new SimpleLifecycleScopeProvider(initialValue);
    }

    @Override
    public Observable<SimpleLifecycle> lifecycle() {
        return lifecycleSubject.hide();
    }

    @Override
    public CorrespondingEventsFunction<SimpleLifecycle> correspondingEvents() {
        return simpleLifecycle -> {
            switch (simpleLifecycle) {
                case STARTED:
                    return SimpleLifecycle.STOPPED;
                case STOPPED:
                    throw new LifecycleEndedException();
                default:
                    throw new IllegalStateException("Unknown lifecycle event.");
            }
        };
    }

    @Override
    public SimpleLifecycle peekLifecycle() {
        return lifecycleSubject.getValue();
    }

    @Override
    public CompletableSource requestScope() {
        return LifecycleScopes.resolveScopeFromLifecycle(this);
    }

    /**
     * Start the simple lifecycle.
     */
    public void start() {
        lifecycleSubject.onNext(SimpleLifecycle.STARTED);
    }

    /**
     * Stop the simple lifecycle.
     */
    public void stop() {
        if (lifecycleSubject.getValue() != SimpleLifecycle.STARTED) {
            throw new IllegalStateException("Attempting to stop lifecycle before starting it.");
        }
        lifecycleSubject.onNext(SimpleLifecycle.STOPPED);
    }

    /**
     * Stop the simple lifecycle.
     */
    public void tryStop() {
        try {
            stop();
        } catch (Exception e) {

        }
    }

    public enum SimpleLifecycle {
        STARTED,
        STOPPED
    }
}
